home *** CD-ROM | disk | FTP | other *** search
/ AI Game Programming Wisdom / AIGameProgrammingWisdom.iso / SourceCode / 11 Learning / 08 Manslow / TanksView.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-02  |  17.9 KB  |  460 lines

  1. //Tanks
  2. //Copyright John Manslow
  3. //29/09/2001
  4.  
  5. // TanksView.cpp : implementation of the CTanksView class
  6. //
  7.  
  8. #include "stdafx.h"
  9. #include "Tanks.h"
  10.  
  11. #include "TanksDoc.h"
  12. #include "TanksView.h"
  13.  
  14. #include "CWorld.h"
  15. #include "CProjectile.h"
  16. #include "CTank.h"
  17. #include "CMLP.h"
  18. #include "CConditionalDistribution.h"
  19. #include "CUnconditionalDistribution.h"
  20. #include "math.h"
  21. #include "fstream.h"
  22.  
  23. #ifdef _DEBUG
  24. #define new DEBUG_NEW
  25. #undef THIS_FILE
  26. static char THIS_FILE[] = __FILE__;
  27. #endif
  28.  
  29. /////////////////////////////////////////////////////////////////////////////
  30. // CTanksView
  31.  
  32. //Too many of these warnings for them to be useful in this file!
  33. #pragma warning(disable:4244)
  34.  
  35. IMPLEMENT_DYNCREATE(CTanksView, CView)
  36.  
  37. BEGIN_MESSAGE_MAP(CTanksView, CView)
  38.     //{{AFX_MSG_MAP(CTanksView)
  39.     ON_WM_TIMER()
  40.     ON_WM_LBUTTONUP()
  41.     //}}AFX_MSG_MAP
  42. END_MESSAGE_MAP()
  43.  
  44. //See TanksDoc for explanations of these
  45. extern BOOL boGeneratingErrorTrainingData;
  46. extern CWorld *pWorld;
  47. extern CMLP *pMLP;
  48.  
  49. extern CConditionalDistribution *pErrorDistribution;
  50. extern CUnconditionalDistribution *pUnconditionalErrorDistribution;
  51.  
  52. extern double *pdMax,*pdMin;
  53. extern double dErrorMin,dErrorMax;
  54. extern double *pdErrorInputMin,*pdErrorInputMax;
  55.  
  56. /////////////////////////////////////////////////////////////////////////////
  57. // CTanksView construction/destruction
  58.  
  59. CTanksView::CTanksView()
  60. {
  61. }
  62.  
  63. CTanksView::~CTanksView()
  64. {
  65. }
  66.  
  67. BOOL CTanksView::PreCreateWindow(CREATESTRUCT& cs)
  68. {
  69.     // TODO: Modify the Window class or styles here by modifying
  70.     //  the CREATESTRUCT cs
  71.  
  72.     return CView::PreCreateWindow(cs);
  73. }
  74.  
  75. /////////////////////////////////////////////////////////////////////////////
  76. // CTanksView drawing
  77.  
  78. void CTanksView::OnDraw(CDC* pDC)
  79. {
  80.     CTanksDoc* pDoc = GetDocument();
  81.     ASSERT_VALID(pDoc);
  82.  
  83.     //Get the size of the window. Useful for drawing the terrain at the bottom of the window and
  84.     //centreing the wind speed arrow in it.
  85.     CRect rect;
  86.     GetClientRect(&rect);
  87.     int x = (rect.Width());
  88.     int y = (rect.Height());
  89.  
  90.     //Draw the terrain at the bottom of the window.
  91.     unsigned long i;
  92.     for(i=0;i<pWorld->ulTerrainResolution;i++)
  93.     {
  94.         pDC->MoveTo(i,y);
  95.         pDC->LineTo(i,y-pWorld->plHeight[i]);
  96.     }
  97.  
  98.     //The sign variable is used to reverse the direction of the wind speed arrow
  99.     //for "negative" wind speeds - i.e. winds blowing from right to left.
  100.     double dSign=1.0;
  101.     if(pWorld->dWindSpeed<0) 
  102.     {
  103.         dSign=-1;
  104.     }
  105.  
  106.     //Draw the centre-aligned wind speed arrow with a length proportional to the
  107.     //wind speed
  108.     pDC->Rectangle(0.5*x,50,15*dSign+0.5*x+100.0*pWorld->dWindSpeed,51);
  109.     pDC->MoveTo(0.5*x,50);
  110.     pDC->LineTo(0.5*x-10*dSign,55);
  111.     pDC->MoveTo(0.5*x,50);
  112.     pDC->LineTo(0.5*x-10*dSign,45);
  113.     pDC->MoveTo(0.5*x+5*dSign+00.0*pWorld->dWindSpeed,50);
  114.     pDC->LineTo(0.5*x-5*dSign+00.0*pWorld->dWindSpeed,55);
  115.     pDC->MoveTo(0.5*x+5*dSign+00.0*pWorld->dWindSpeed,50);
  116.     pDC->LineTo(0.5*x-5*dSign+00.0*pWorld->dWindSpeed,45);
  117.     pDC->MoveTo(15*dSign+0.5*x-0*dSign+100.0*pWorld->dWindSpeed,50);
  118.     pDC->LineTo(15*dSign+0.5*x-10*dSign+100.0*pWorld->dWindSpeed,55);
  119.     pDC->MoveTo(15*dSign+0.5*x-0*dSign+100.0*pWorld->dWindSpeed,50);
  120.     pDC->LineTo(15*dSign+0.5*x-10*dSign+100.0*pWorld->dWindSpeed,45);
  121.  
  122.     //If a projectile exists, draw it as a rectangle
  123.     if(pWorld->pProjectile!=NULL)
  124.         pDC->Rectangle(pWorld->pProjectile->dxPosition-4,y-pWorld->pProjectile->dyPosition-2,
  125.             pWorld->pProjectile->dxPosition,y-pWorld->pProjectile->dyPosition+2);
  126.  
  127.     //All shapes drawn after this point are filled black
  128.     pDC->SelectStockObject(BLACK_BRUSH);
  129.  
  130.     //Define the exact position for the player's tank graphic.
  131.     long tx=long(pWorld->ppPlayer[0]->dxPosition-10.0);
  132.     long ty=long(y-pWorld->ppPlayer[0]->dyPosition+1.0);
  133.  
  134.     //Can be used to scale the tank graphics. Should be left at 4.0 since this
  135.     //is the length for which collision detection is calibrated.
  136.     double dScale=4.0;
  137.  
  138.     //Draw the tank's wheels
  139.     pDC->Ellipse(tx,ty,dScale+tx,-dScale+ty);
  140.     pDC->Ellipse(dScale+tx,ty,2*dScale+tx,-dScale+ty);
  141.     pDC->Ellipse(2*dScale+tx,ty,3*dScale+tx,-dScale+ty);
  142.     pDC->Ellipse(3*dScale+tx,ty,4*dScale+tx,-dScale+ty);
  143.     pDC->Ellipse(4*dScale+tx,-0.25*dScale+ty,5*dScale+tx,-1.25*dScale+ty);
  144.  
  145.     //Draw its hull
  146.     pDC->Rectangle(tx,-dScale+ty,4.5*dScale+tx,-1.5*dScale+ty);
  147.     pDC->Rectangle(tx,-1.5*dScale+ty,2*dScale+tx,-2.5*dScale+ty);
  148.     CPoint Poly[3];
  149.     Poly[0].x=tx+2*dScale;
  150.     Poly[0].y=ty-1.5*dScale;
  151.     Poly[1].x=tx+2.5*dScale;
  152.     Poly[1].y=ty-1.5*dScale;
  153.     Poly[2].x=tx+2*dScale;
  154.     Poly[2].y=ty-2.5*dScale;
  155.     pDC->Polygon(Poly,3);
  156.  
  157.     //Draw its barrel at the correct angle 
  158.     CPoint Barrel[4];
  159.     Barrel[0].x=tx+2.0*dScale-0.1*pWorld->ppPlayer[0]->dBarrely*dScale;;
  160.     Barrel[0].y=ty-2.0*dScale-0.1*pWorld->ppPlayer[0]->dBarrelx*dScale;
  161.     Barrel[1].x=tx+2.0*dScale+0.1*pWorld->ppPlayer[0]->dBarrely*dScale;;
  162.     Barrel[1].y=ty-2.0*dScale+0.1*pWorld->ppPlayer[0]->dBarrelx*dScale;
  163.     Barrel[2].x=tx+2.0*dScale+3.9*pWorld->ppPlayer[0]->dBarrelx*dScale;;
  164.     Barrel[2].y=ty-2.0*dScale-4.1*pWorld->ppPlayer[0]->dBarrely*dScale;
  165.     Barrel[3].x=tx+2.0*dScale+4.1*pWorld->ppPlayer[0]->dBarrelx*dScale;;
  166.     Barrel[3].y=ty-2.0*dScale-3.9*pWorld->ppPlayer[0]->dBarrely*dScale;
  167.     pDC->Polygon(Barrel,4);
  168.  
  169.     //Define exact position of AI tank
  170.     tx=pWorld->ppPlayer[1]->dxPosition+10;
  171.     ty=y-pWorld->ppPlayer[1]->dyPosition+1;
  172.     
  173.     //Render it
  174.     pDC->Ellipse(tx,ty,-dScale+tx,-dScale+ty);
  175.     pDC->Ellipse(-dScale+tx,ty,-2*dScale+tx,-dScale+ty);
  176.     pDC->Ellipse(-2*dScale+tx,ty,-3*dScale+tx,-dScale+ty);
  177.     pDC->Ellipse(-3*dScale+tx,ty,-4*dScale+tx,-dScale+ty);
  178.     pDC->Ellipse(-4*dScale+tx,-0.25*dScale+ty,-5*dScale+tx,-1.25*dScale+ty);
  179.     pDC->Rectangle(tx,-dScale+ty,-4.5*dScale+tx,-1.5*dScale+ty);
  180.     pDC->Rectangle(tx,-1.5*dScale+ty,-2*dScale+tx,-2.5*dScale+ty);
  181.  
  182.     Poly[0].x=tx-2*dScale;
  183.     Poly[0].y=ty-1.5*dScale;
  184.     Poly[1].x=tx-2.5*dScale;
  185.     Poly[1].y=ty-1.5*dScale;
  186.     Poly[2].x=tx-2*dScale;
  187.     Poly[2].y=ty-2.5*dScale;
  188.     pDC->Polygon(Poly,3);
  189.  
  190.     Barrel[0].x=tx-2.0*dScale+0.1*pWorld->ppPlayer[1]->dBarrely*dScale;;
  191.     Barrel[0].y=ty-2.0*dScale-0.1*pWorld->ppPlayer[1]->dBarrelx*dScale;
  192.     Barrel[1].x=tx-2.0*dScale-0.1*pWorld->ppPlayer[1]->dBarrely*dScale;;
  193.     Barrel[1].y=ty-2.0*dScale+0.1*pWorld->ppPlayer[1]->dBarrelx*dScale;
  194.     Barrel[2].x=tx-2.0*dScale+3.9*pWorld->ppPlayer[1]->dBarrelx*dScale;;
  195.     Barrel[2].y=ty-2.0*dScale-4.1*pWorld->ppPlayer[1]->dBarrely*dScale;
  196.     Barrel[3].x=tx-2.0*dScale+4.1*pWorld->ppPlayer[1]->dBarrelx*dScale;;
  197.     Barrel[3].y=ty-2.0*dScale-3.9*pWorld->ppPlayer[1]->dBarrely*dScale;
  198.     pDC->Polygon(Barrel,4);
  199.  
  200.     //Prepare storage for scores
  201.     char *pString=new char[100];
  202.  
  203.     //Dispplay the player's score in the top left of the window.
  204.     sprintf(pString,"%0.0lf",pWorld->dScore[0]);
  205.     pDC->TextOut(25,20,pString);
  206.  
  207.     //Display the AI's score in the top right.
  208.     sprintf(pString,"%3.0lf",pWorld->dScore[1]);
  209.     pDC->TextOut(715,20,pString);
  210.  
  211.     //Clean up.
  212.     delete []pString;
  213.  
  214.     //Set the timer to generate ticks every 10 ms or so to update the game world
  215.     SetTimer(10,10,NULL);
  216. }
  217.  
  218. /////////////////////////////////////////////////////////////////////////////
  219. // CTanksView diagnostics
  220.  
  221. #ifdef _DEBUG
  222. void CTanksView::AssertValid() const
  223. {
  224.     CView::AssertValid();
  225. }
  226.  
  227. void CTanksView::Dump(CDumpContext& dc) const
  228. {
  229.     CView::Dump(dc);
  230. }
  231.  
  232. CTanksDoc* CTanksView::GetDocument() // non-debug version is inline
  233. {
  234.     ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CTanksDoc)));
  235.     return (CTanksDoc*)m_pDocument;
  236. }
  237. #endif //_DEBUG
  238.  
  239. /////////////////////////////////////////////////////////////////////////////
  240. // CTanksView message handlers
  241.  
  242. void CTanksView::OnTimer(UINT nIDEvent) 
  243. {
  244.     CTanksDoc* pDoc = GetDocument();
  245.  
  246.     //If an event (e.g. a collision) occured in the game world, clear the window contents and
  247.     //redraw. The TimeSptep function returns TRUE if an event occurred.
  248.     if(pWorld->TimeStep())
  249.     {
  250.         //Clear the screen
  251.         CClientDC *pDC=new CClientDC(this);
  252.         pDC->Rectangle(0,0,1000,1000);
  253.         delete pDC;
  254.     }
  255.  
  256.     //Tell Windows that the window contents will need to be redraw but not to clear the window contents first
  257.     Invalidate(FALSE);
  258.  
  259.     //If there's no projectile in the world...
  260.     if(!pWorld->pProjectile)
  261.     {
  262.         //...and its the AI's turn to fire,
  263.         if(pWorld->nActivePlayer==1)
  264.         {
  265.             //Get the size of the window
  266.             CRect rect;
  267.             GetClientRect(&rect);
  268.             int x = (rect.Width());
  269.             int y = (rect.Height());
  270.  
  271.             //If we're not generating training data (i.e. this is a normal game)
  272.             if(!boGeneratingErrorTrainingData)
  273.             {
  274.                 //Inside this if statement is where the AI uses the barrel angle calculating neural network 
  275.                 //to compute the optimal barrel angle, and the unconditional and conditional distribution models
  276.                 //to add random variation.
  277.                 pWorld->ulShotNumber++;
  278.  
  279.                 //Allocate space for the three barrel angle neural network inputs (x-displacement between player 
  280.                 //and AI tanks, y-displacement and wind speed)
  281.                 double dInputs[3];
  282.  
  283.                 //Introduce temporary variables to store the relative displacements of the tanks
  284.                 //in the game world
  285.                 double dxDisplacement=-pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition+pWorld->ppPlayer[1-pWorld->nActivePlayer]->dxPosition;
  286.                 double dyDisplacement=-pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition+pWorld->ppPlayer[1-pWorld->nActivePlayer]->dyPosition;
  287.  
  288.                 //Set the network inputs to scaled versions of x-displacement, y-displacement and
  289.                 //wind speed. Scaling factors are calculated from the example data and ensure that
  290.                 //all network inputs lie roughly in the range -1 to +1. Scaling is not strictly 
  291.                 //necessary but can often reduce the time needed to train the network. You MUST be 
  292.                 //consistent with scaling: the same scaling factors that were used during training
  293.                 //MUST be used to here
  294.                 dInputs[0]=2.0*((dxDisplacement-pdMin[0])/(pdMax[0]-pdMin[0])-0.5);
  295.                 dInputs[1]=2.0*((dyDisplacement-pdMin[1])/(pdMax[1]-pdMin[1])-0.5);
  296.                 dInputs[2]=2.0*((pWorld->dWindSpeed-pdMin[2])/(pdMax[2]-pdMin[2])-0.5);
  297.  
  298.                 //Pass the inputs to the network and compute its output.
  299.                 double *pdOutputs=pMLP->pdGetOutputs(dInputs);
  300.  
  301.                 //Prepare to get the random aiming error
  302.                 double dAngularError=0.0;
  303.  
  304.                 //This will point to the vector of bin probabilities and is only used to plot the error distribution on-screen
  305.                 double *pdErrorOutputs;
  306.  
  307.                 //If this is the first shot in a new game,
  308.                 if(pWorld->ulShotNumber==1)
  309.                 {
  310.                     //Choose an aiming error by randomly sampling from the unconditional distribution
  311.                     dAngularError=pUnconditionalErrorDistribution->dGetOutputs()*(dErrorMax-dErrorMin)+dErrorMin;
  312.  
  313.                     //Get the bin probabilities so we can plot them on the screen
  314.                     pdErrorOutputs=pUnconditionalErrorDistribution->pdBinProbabilities;
  315.                 }
  316.                 else
  317.                 {
  318.                     //Otherwise, choose it by sampling from the conditional distribution, which takes into account the error
  319.                     //made on the preceding shot.
  320.  
  321.                     //The input to the conditional aiming error distribution is a scaled version of the aiming error made on the
  322.                     //preceding shot. Again, the scaling must be consistent with that used when the distribution was trained.
  323.                     double dErrorDistributionInput=2.0*((pWorld->dLastAngularError-dErrorMin)/(dErrorMax-dErrorMin)-0.5);
  324.  
  325.                     //Get a sample from the conditional error distribution and descale it
  326.                     dAngularError=(pErrorDistribution->dGetOutputs(&dErrorDistributionInput))*(dErrorMax-dErrorMin)+dErrorMin;
  327.  
  328.                     //Get the bin probabilities so we can plot them on the screen
  329.                     pdErrorOutputs=pErrorDistribution->pdGetBinProbabilities(&dErrorDistributionInput);
  330.                 }
  331.                 //This error will be the "last error" on the next turn
  332.                 pWorld->dLastAngularError=dAngularError;
  333.  
  334.                 //Add the aiming error to the optimal barrel angle recommnded by the barrel angle neural network
  335.                 pWorld->ppPlayer[pWorld->nActivePlayer]->dInclination=pdOutputs[0]+dAngularError;
  336.  
  337.                 ////Draw the bar-graph of the error distribution (just for curiosity)
  338.                 CClientDC *pDC=new CClientDC(this);
  339.                 pDC->Rectangle(0,0,1000,1000);
  340.                 for(unsigned long i=0;i<pErrorDistribution->ulNumberOfOutputs;i++)
  341.                 {
  342.                     pDC->Rectangle(100+i*4,250,104+i*4,250-400*pdErrorOutputs[i]);
  343.                 }
  344.                 if(pWorld->ulShotNumber!=1)
  345.                 {
  346.                     //Don't delete this in the case of the unconditional distribution because it points to
  347.                     //a member variable itself rather than a copy of it
  348.                     delete []pdErrorOutputs;
  349.                 }
  350.                 delete pDC;
  351.  
  352.                 //Clean up the barrel angle neural network's outputs
  353.                 delete []pdOutputs;
  354.  
  355.                 //Set the AI tank's barrel to the chosen angle
  356.                 pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx=sin(pWorld->ppPlayer[pWorld->nActivePlayer]->dInclination);
  357.                 pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely=cos(pWorld->ppPlayer[pWorld->nActivePlayer]->dInclination);
  358.  
  359.                 //Create a new projectile with location (roughly) at the end of the AI
  360.                 //tank's barrel, and direction given by its inclination.
  361.                 pWorld->pProjectile=new CProjectile(pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition+18*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx+4,
  362.                     pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition+18*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely+7,
  363.                     9*sin(pWorld->ppPlayer[pWorld->nActivePlayer]->dInclination),
  364.                     9*cos(pWorld->ppPlayer[pWorld->nActivePlayer]->dInclination));
  365.  
  366.             }
  367.             else
  368.             {
  369.                 pWorld->nActivePlayer=0;
  370.             }
  371.         }
  372.     }
  373.     CView::OnTimer(nIDEvent);
  374. }
  375.  
  376. void CTanksView::OnLButtonUp(UINT nFlags, CPoint point) 
  377. {
  378.     CTanksDoc* pDoc = GetDocument();
  379.  
  380.     //If the active player is not the AI and a projectile does not already exist
  381.     //in the world,
  382.     if(pWorld->nActivePlayer==0 && !pWorld->pProjectile)
  383.     {
  384.         //Get the size of the window
  385.         CRect rect;
  386.         GetClientRect(&rect);
  387.         int x = (rect.Width());
  388.         int y = (rect.Height());
  389.  
  390.         //Use the location of the mouse click relative to the player's tank to
  391.         //set the inclination of the player's tank's barrel. If we're not generating error training data,
  392.         //the player is controlling the usual (lefthand) tank. Otherwise, the player is controlling
  393.         //the AI tank (the rightmost one)
  394.         if(!boGeneratingErrorTrainingData)
  395.         {
  396.             //Work out the direction of the player tank's barrel
  397.             pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx=(point.x-pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition)
  398.                 /sqrt(pow(point.x-pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition,2.0)+pow(point.y-y+pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition,2.0));
  399.             pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely=-(point.y-y+pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition)
  400.                 /sqrt(pow(point.x-pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition,2.0)+pow(point.y-y+pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition,2.0));
  401.  
  402.             //Create a new projectile
  403.             pWorld->pProjectile=new CProjectile(
  404.                 pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition+18*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx,
  405.                 pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition+18*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely+6,
  406.                 9*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx,
  407.                 9*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely);
  408.         }
  409.         else
  410.         {
  411.             //Work out the angle of the AI tank's barrel (if we're generating aiming error data, the player is controlling
  412.             //the AI tank)
  413.             pWorld->ppPlayer[1-pWorld->nActivePlayer]->dBarrelx=(point.x-pWorld->ppPlayer[1-pWorld->nActivePlayer]->dxPosition)
  414.                 /sqrt(pow(point.x-pWorld->ppPlayer[1-pWorld->nActivePlayer]->dxPosition,2.0)+pow(point.y-y+pWorld->ppPlayer[1-pWorld->nActivePlayer]->dyPosition,2.0));
  415.             pWorld->ppPlayer[1-pWorld->nActivePlayer]->dBarrely=-(point.y-y+pWorld->ppPlayer[1-pWorld->nActivePlayer]->dyPosition)
  416.                 /sqrt(pow(point.x-pWorld->ppPlayer[1-pWorld->nActivePlayer]->dxPosition,2.0)+pow(point.y-y+pWorld->ppPlayer[1-pWorld->nActivePlayer]->dyPosition,2.0));
  417.  
  418.             //Increment the shot count
  419.             pWorld->ulShotNumber++;
  420.  
  421.             //Make sure the AI doesn't get a turn
  422.             pWorld->nActivePlayer=1-pWorld->nActivePlayer;
  423.  
  424.             //Create a new projectile
  425.             pWorld->pProjectile=new CProjectile(
  426.                 pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition+18*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx+2,
  427.                 pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition+18*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely+7,
  428.                 9*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx,
  429.                 9*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely);
  430.  
  431.             //We need to know how the tank should have aimed in order to calculate the aiming error. To this end,
  432.             //we now use the barrel angle neural network to calculate the optimal barrel angle:
  433.             double Inputs[3];
  434.             double dxDisplacement=-pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition+pWorld->ppPlayer[1-pWorld->nActivePlayer]->dxPosition;
  435.             double dyDisplacement=-pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition+pWorld->ppPlayer[1-pWorld->nActivePlayer]->dyPosition;
  436.  
  437.             //Set up the scaled inputs to the neural network (x-displacement, y-displacement, wind speed)
  438.             Inputs[0]=2.0*((dxDisplacement-pdMin[0])/(pdMax[0]-pdMin[0])-0.5);
  439.             Inputs[1]=2.0*((dyDisplacement-pdMin[1])/(pdMax[1]-pdMin[1])-0.5);
  440.             Inputs[2]=2.0*((pWorld->dWindSpeed-pdMin[2])/(pdMax[2]-pdMin[2])-0.5);
  441.  
  442.             //Pass the inputs to the network and compute its output - the optimal barrel angle
  443.             double *pdOutputs=pMLP->pdGetOutputs(Inputs);
  444.  
  445.             //Work out the angle that the player chose
  446.             pWorld->ppPlayer[pWorld->nActivePlayer]->dInclination=asin(pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx/sqrt(pow(pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx,2.0)+pow(pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely,2.0)));
  447.  
  448.             //Calculate the aiming error as the difference between the two. This error information will be written
  449.             //to a data file in the world class when the projectile is destroyed (i.e. lands, goes out of range, etc.)
  450.             pWorld->dAngularError=pWorld->ppPlayer[pWorld->nActivePlayer]->dInclination-pdOutputs[0];
  451.  
  452.             //Clean up the barrel angle neural network's outputs
  453.             delete []pdOutputs;
  454.         }
  455.         //Redraw the game world with the tank's barrel in its new position
  456.         Invalidate(TRUE);
  457.     }
  458.  
  459.     CView::OnLButtonUp(nFlags, point);
  460. }